import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;

import java.io.*;
import java.util.ArrayList;

public class TransVersion {

    public static  String readFromTextFile(String pathname) throws IOException {
        String code="";
        File filename = new File(pathname);
        InputStreamReader reader = new InputStreamReader(new FileInputStream(filename));
        BufferedReader br = new BufferedReader(reader);
        String line = "";
        line = br.readLine();
        while(line != null) {
            code+=line+"\n";
            line = br.readLine();
        }
        return code;
    }
    public static void GenAADL(ArrayList<FuncToAADL> ftlist) throws IOException {
        int count=0;
        String rootPath="H:\\aadl_thread_gen";



        for(FuncToAADL ft:ftlist)
        {
            PrintStream psOld = System.out;
            String aadlPath=rootPath+"\\"+count+".aadl";
            System.out.println(aadlPath);
            File f = new File(aadlPath);
            if(!f.exists()){
                f.createNewFile();
            }
            System.setOut(new PrintStream(f));

            System.out.println("thread "+ft.getName().strip());
            System.out.println("\tfeatures");
            for(String paramName:ft.getParamName())
            {
                System.out.println("\t\t"+paramName+": in data port;");
            }
            System.out.println("\t\t"+ft.getRetType()+":out event data port");
            System.out.println("\tproperties");
            for(int i=0;i<ft.getParamName().size();i++)
            {
                System.out.println("\t\tParam_Property::param_type=>\""+ft.getParamType().get(i)+"\" applies to "+ft.getParamName().get(i)+";");
            }
            System.out.println("\t\tParam_Property::return_type=>\""+ft.getRetType()+"\" applies to "+ft.getRetType()+";");
            System.out.println("end "+ft.getName().strip());
            System.out.println("thread implementation "+ft.getName().strip()+".impl");
            System.out.println("end "+ft.getName().strip()+".impl;");
            count++;
            System.setOut(psOld);
        }

    }
    public static void getParamAndType(StringBuffer sb,FuncToAADL ft)
    {
        ArrayList<String> paramType=new ArrayList<>();
        ArrayList<String> paramName=new ArrayList<>();
        String[] params=sb.toString().split(",");
        if(params.length>0)
        {
            for(String s:params)
            {
                String[] sp=s.split(" ");
                if(sp.length>0)
                {
                    System.out.print("type:");
                    StringBuffer type=new StringBuffer();
                    for(int i=0;i<sp.length-1;i++)
                    {
                        System.out.print(sp[i]+" ");
                        type.append(sp[i]+" ");

                    }
                    paramType.add(type.toString());
                    System.out.println();
                    System.out.println("param:"+sp[sp.length-1]);
                    paramName.add(sp[sp.length-1]);
                }
            }
        }
        ft.setParamName(paramName);
        ft.setParamType(paramType);
    }
    public static void findName(Tree tree,StringBuffer sb)
    {
        TreeProcessor tp=new TreeProcessor();

        if(tree.getChild(0).getChild(0).getChild(0).getRootData().equals("noptrdeclarator")&&tree.getChildCount()==1)
        {//find call function,make it return value

            tp.printCodeToSb(tree.getChild(0).getChild(0).getChild(0),sb);

            System.out.println("function name:"+sb.toString().strip());
            //System.out.println(sb);
        }



    }
    public static void findParamList(Tree tree,StringBuffer sb)
    {
        TreeProcessor tp=new TreeProcessor();
        if(!tree.isEmpty()) {
            if (tree.getRootData().equals("parametersandqualifiers") && (tree.getChildCount() ==3||tree.getChildCount()==4))
            {//find call function,make it return value
                if(tree.getChild(1).getChildCount()>0)
                {
                    tp.printParamListToSb(tree.getChild(1), sb);
                }
            }
            for(Tree child : tree.getChilds())
            {
                if(child != null)
                {
                    findParamList(child,sb);
                }
            }
        }
    }
    public static void FindDeclaration(Tree tree,ArrayList<FuncToAADL> ftlist) throws IOException {
        TreeProcessor tp=new TreeProcessor();
        if(!tree.isEmpty()) {

            if(tree.getRootData().equals("memberdeclaration")&&tree.getChildCount()==3)
            {

                StringBuffer sb=new StringBuffer();
                StringBuffer retType=new StringBuffer();
                StringBuffer paramList=new StringBuffer();

                FuncToAADL ft=new FuncToAADL();
                if(tree.getChild(0).getRootData().equals("declspecifierseq"))
                {

                    if(tree.getChild(0).getChildCount()==2)
                    {
                        tp.printCodeToSb(tree.getChild(0).getChild(1),retType);
                        //System.out.println("return type:"+retType.toString());
                        ft.setRetType(retType.toString());
                    }
                    else if(tree.getChild(0).getChildCount()==1)
                    {
                        tp.printCodeToSb(tree.getChild(0),retType);
                        //System.out.println("return type:"+retType.toString());
                        ft.setRetType(retType.toString());
                    }


                }
                if(tree.getChild(1).getChild(0).getChild(0).getRootData().equals("declarator"))
                {
                    findParamList(tree.getChild(1).getChild(0).getChild(0),paramList);
                    //System.out.println("paramList:"+paramList);
                    getParamAndType(paramList,ft);
                    findName(tree.getChild(1).getChild(0).getChild(0),sb);
                    ft.setName(sb.toString().strip());

                }
            }
            if(tree.getRootData().equals("functiondefinition")&&tree.getChildCount()==3) {//find call function,make it return value

                StringBuffer sb = new StringBuffer();
                StringBuffer retType = new StringBuffer();
                StringBuffer paramList = new StringBuffer();

                FuncToAADL ft = new FuncToAADL();

                tp.getFunctionName(tree, sb);
                ft.setName(sb.toString().strip());
                System.out.println("function name:" + sb.toString().strip());
                if (tree.getChild(0).getRootData().equals("declspecifierseq")) {
                    if (tree.getChild(0).getChildCount() == 2) {
                        tp.printCodeToSb(tree.getChild(0).getChild(1), retType);
                        //System.out.println("return type:"+retType.toString());
                        ft.setRetType(retType.toString());
                    } else if (tree.getChild(0).getChildCount() == 1) {
                        tp.printCodeToSb(tree.getChild(0), retType);
                        //System.out.println("return type:"+retType.toString());
                        ft.setRetType(retType.toString());
                    }
                }
                if (tree.getChild(1).getRootData().equals("declarator")) {
                    findParamList(tree.getChild(1), paramList);
                    //System.out.println("paramList:"+paramList);
                    getParamAndType(paramList, ft);
                    //findName(tree.getChild(1),sb);


                }
                /*if(tree.getChild(2).getRootData().equals("functionbody"))
                {
                    String rootdir="H:\\aadl_lib\\srl_src";
                    String printPath=rootdir+"\\"+sb.toString().strip()+".cpp";
                    System.out.println(printPath);
                    ft.setCppPath(printPath);
                    PrintStream psOld = System.out;
                    File f = new File(printPath);
                    if(!f.exists()){
                        f.createNewFile();
                    }
                    System.setOut(new PrintStream(f));
                    tp.printCodeToFile(tree);
                    System.setOut(psOld);
                }*/
                ftlist.add(ft);

            }

            for(Tree child : tree.getChilds())
            {
                if(child != null)
                {
                    FindDeclaration(child,ftlist);
                }
            }
        }
    }

    public static void GetFileInterface(String loadPath,ArrayList<FuncToAADL> ftlist) throws IOException {
        TreeProcessor tp=new TreeProcessor();
        String code=readFromTextFile(loadPath);

        ANTLRInputStream input = new ANTLRInputStream(code);
        CPP14Lexer lexer = new CPP14Lexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        CPP14Parser parser = new CPP14Parser(tokens);
        CPP14Parser.TranslationunitContext tree = parser.translationunit();

        tree.removeLastChild();
        //System.out.println(tree.getText());
        //visitorTree(tree4,parser4);
        Tree myTree=new Tree(tp.getRuleName(tree,parser));
        tp.convertTree(tree,parser,myTree);



        FindDeclaration(myTree,ftlist);



    }
    public static void GetAllFilePath(String rosdir, ArrayList<String> pathlist)
    {

        File file = new File(rosdir);
        File[] ros_fs = file.listFiles();
        for(File f:ros_fs)
        {

            if(f.isDirectory())
            {
                System.out.println("——"+f);
                GetAllFilePath(f.getPath(),pathlist);
            }
            else {
                System.out.println("—— ——"+f);
                pathlist.add(f.getPath());
            }

        }

    }

    public static void GetRosInterface(String rosdir,ArrayList<FuncToAADL> interfacelist) throws IOException
    {


        ArrayList<String> pathlist=new ArrayList<>();
        GetAllFilePath(rosdir, pathlist);

        for(String path:pathlist)
        {
            System.out.println(path);


            //GetFileInterface("H:\\ros\\transport\\transport.h",ftlist);
            GetFileInterface(path,interfacelist);
        }
        System.out.println(interfacelist.size());


    }

    public static boolean isRosInterface(ArrayList<FuncToAADL> interfacelist,String callfuncname,ArrayList<String> paramlist)
    {
        for(FuncToAADL ft:interfacelist)
        {
            if(ft.getName().equals(callfuncname))
            {
                System.out.println("same name: "+callfuncname);
                if(ft.getParamType().size()==paramlist.size())
                {
                    System.out.println("same param.");
                    return true;
                }
            }
        }
        return false;
    }

    public static void FindCallFunc(Tree tree,ArrayList<FuncToAADL> interfacelist)
    {
        TreeProcessor tp=new TreeProcessor();
        Tree ret=new Tree();
        if(!tree.isEmpty()) {
            if(tree.getRootData().equals("postfixexpression")&&tree.getChildCount()==4&&tree.getChild(1).getRootData().equals("("))
            {
                System.out.println("Call a function.");
                Tree l = tree.getWantedChild(tree.getChild(0), "unqualifiedid");
                String callfuncname = l.getRootData().toString();
                ArrayList<String> paramlist=tp.getCalledParams(tree);
                if(isRosInterface(interfacelist,callfuncname,paramlist))
                {
                    System.out.println("use Ros Interface.");
                    tp.printCode(tree);
                    System.out.println();
                    Tree addtree=new Tree("/*this statement call a ros function.*/");
                    tree.addNode(addtree);
                    tp.printCode(tree);
                    System.out.println();
                }
            }

            for(Tree child : tree.getChilds()) {
                if(child != null) {
                    FindCallFunc(child,interfacelist);
                }
            }
        }

    }
    public static void ModifyCppFuncs(String loadPath,ArrayList<FuncToAADL> interfacelist) throws IOException {
        TreeProcessor tp=new TreeProcessor();
        String code=readFromTextFile(loadPath);

        ANTLRInputStream input = new ANTLRInputStream(code);
        CPP14Lexer lexer = new CPP14Lexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        CPP14Parser parser = new CPP14Parser(tokens);
        CPP14Parser.TranslationunitContext tree = parser.translationunit();

        tree.removeLastChild();
        //System.out.println(tree.getText());
        //visitorTree(tree4,parser4);
        Tree myTree=new Tree(tp.getRuleName(tree,parser));
        tp.convertTree(tree,parser,myTree);

        FindCallFunc(myTree,interfacelist);
        StringBuffer sb=new StringBuffer();
        //tp.printCode(myTree);


    }

    public static void main(String[] args) throws IOException {
        String rosdir="H:\\ros";
        ArrayList<FuncToAADL> interfacelist=new ArrayList<>();
        GetRosInterface(rosdir,interfacelist);
        String path="H:\\GIT\\srl_global_planner-master\\src\\srl_global_planner.cpp";
        ModifyCppFuncs(path,interfacelist);
        //ArrayList<FuncToAADL> ftlist=new ArrayList<>();
        //GetFileInterface("H:\\GIT\\srl_global_planner-master\\src\\srl_global_planner.cpp",ftlist);
        //GenAADL(ftlist);
    }



}
